package cn.tedu.charging.order.consumer;
import cn.tedu.charging.common.constant.AmqpDeclareConst;
import cn.tedu.charging.common.constant.CacheKeyConst;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.order.serializer.MessageSerializer;
import cn.tedu.charging.order.service.ConsumerService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 消费入口类,我希望当前项目进程 有很多消息队列监听,都调用
 * 入口类的方法
 */
@Component
@Slf4j
public class AmqpDelayConsumer {
    @Autowired
    private MessageSerializer<DelayCheckMessage> messageSerializer;
    @Autowired
    private ConsumerService consumerService;
    @Autowired
    private RedisTemplate redisTemplate;
    //消费方法重新写 DelayCheckMessage
    //手动确认,调用consumerService处理业务
    @RabbitListener(queues = {AmqpDeclareConst.DELAY_BILL_DL_QUEUE})
    public void delayConsume(Message message,Channel channel){
        //1.解析消息
        DelayCheckMessage msg = messageSerializer.deserialize(message.getBody(), DelayCheckMessage.class);
        //2.调用业务处理人
        //2.1抢锁 set key value NX EX 5
        //设计一个key值,多个线程 操作同一张订单 key相同
        String lockKey= CacheKeyConst.CONSUME_ORDER_LOCK_PREFIX+msg.getOrderNo();
        //从redis客户端拿到操作string的子对象
        ValueOperations valueOps = redisTemplate.opsForValue();
        //2.2抢锁执行
        //判断如果抢到了 就执行业务 没抢到就等待200毫秒,计数最多重抢3次
        Boolean haveLock=false;
        int count=0;//计数器
        try{
            do {
                //第一次直接抢 count>0&&count<4 第234次 等待200ms
                if (count >= 1 && count < 4) {
                    Thread.sleep(200);
                } else if (count >= 4) {
                    log.error("你抢了3次都没抢到,放弃把");
                    return;
                }
                haveLock = valueOps.setIfAbsent(lockKey, "", 5, TimeUnit.SECONDS);
                count++;
            } while (!haveLock);
            log.debug("当前延迟消息线程抢锁成功");
            consumerService.handleCheckNoRes(msg);
        }catch (Exception e) {
            log.error("分布式锁落地过程出现问题",e);
        }finally{
            //释放锁
            redisTemplate.delete(lockKey);
        }
        //3.手动确认
        try{
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            log.error("手动确认失败",e);
        }
    }
}
